package com.nt.train.service.context;
import com.nt.train.annotation.OrderTypeAnnotation;
import com.nt.train.enums.BizResultCode;
import com.nt.train.enums.OrderTypeEnum;
import com.nt.train.exception.BizException;
import com.nt.train.service.OrderStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
 * 根据订单类型返回对应的处理策略
 * @author hlf
 * @version 1.0
 * @date 2022/8/5 13:53
 */
@Component
public class HandlerOrderContext implements ApplicationContextAware {

    private static final Logger logger = LoggerFactory.getLogger(HandlerOrderContext.class);

    /**
     * 锁, 防止重复创建同一对象
     */
    private static final Object lock = new Object();

    /**
     * 创建订单服务策略class集合 <key,value>=<订单类型, 创建订单服务策略class>
     * <p>
     * 注：此集合只存放OrderStrategy的子类class，对应的实例交由spring容器来管理
     */
    private static Map<OrderTypeEnum, Class<? extends OrderStrategy>> orderStrategyBeanMap = new HashMap<>();

    @Autowired
    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        HandlerOrderContext.applicationContext = applicationContext;
    }

    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }


    /**
     * 获取创建订单策略实例
     * @param orderType 订单类型
     * @author chenck
     * @date 2020/2/20 9:53
     */
    public static OrderStrategy getInstance(Integer orderType) {
        if (null == orderType) {
            throw new BizException(BizResultCode.ERR_PARAM.getCode(), "订单类型不能为空");
        }

        OrderTypeEnum orderTypeEnum = OrderTypeEnum.getEnum(orderType);
        if (null == orderTypeEnum) {
            throw new BizException(BizResultCode.ERR_PARAM.getCode(), "暂时不支持该订单类型orderType=" + orderType);
        }
        // 当集合为空时，则初始化
        if (orderStrategyBeanMap.size() == 0) {
            initStrategy();
        }

        Class<? extends OrderStrategy> clazz = orderStrategyBeanMap.get(orderTypeEnum);
        if (null == clazz) {
            throw new BizException(BizResultCode.ERR_PARAM.getCode(), "未找到订单类型(" + orderTypeEnum + ")的创建订单策略实现类");
        }
        //从spring容器中获取bean
        return applicationContext.getBean(clazz);
    }

    /**
     * 初始化
     */
    private static void initStrategy() {
        synchronized (lock) {
            // 获取接口下所有实例bean
            Map<String, OrderStrategy> strategyMap = applicationContext.getBeansOfType(OrderStrategy.class);
            if (null == strategyMap || strategyMap.size() == 0) {
                throw new BizException(BizResultCode.ERR_SYSTEM.getCode(),
                        "代码配置错误：未获取到OrderStrategy的实现类，请检查代码中是否有将实现类bean注册到spring容器");
            }
            // 加载所有策略类对应的配置
            OrderTypeAnnotation annotation;
            for (Map.Entry strategy : strategyMap.entrySet()) {
                Class strategyClazz = strategy.getValue().getClass();
                // 因为策略bean可能是经过动态代理生成的bean实例（可能是多重动态代理后的代理对象），
                // 故而bean实例的class可能已经不是原来的class了，所以beanClass.getAnnotation(...)获取不到对应的注解元信息
                annotation = (OrderTypeAnnotation) strategyClazz.getAnnotation(OrderTypeAnnotation.class);
                if (null == annotation) {
                    // 当从bean实例的class上获取不到注解元信息时，通过AnnotationUtils工具类递归来获取
                    annotation = AnnotationUtils.findAnnotation(strategyClazz, OrderTypeAnnotation.class);
                    if (null == annotation) {
                        logger.warn("代码配置错误：创建订单策略实现类{}未配置OrderTypeAnnotation注解", strategyClazz.getName());
                        continue;
                    }
                }
                // 支持多个事件类型
                OrderTypeEnum typeEnum = annotation.orderType();
                //String key = getKey(typeEnum.getOrderType());
                if (orderStrategyBeanMap.containsKey(typeEnum)) {
                    logger.error("代码配置错误：一个订单类型({})只能对应一个创建订单策略实现{}", typeEnum, strategyClazz.getName());
                    throw new BizException(BizResultCode.ERR_SYSTEM.getCode(), "代码配置错误：一个订单类型(" + typeEnum
                            + ")只能对应一个创建订单策略实现bean");
                }
                orderStrategyBeanMap.put(typeEnum, strategyClazz);
            }
            if (orderStrategyBeanMap.size() == 0) {
                logger.warn("初始化创建订单策略集合失败");
            }
        }
    }
}